package net.jlibrary.account.service;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.event.spi.PostCommitInsertEventListener;
import org.hibernate.event.spi.PostCommitUpdateEventListener;
import org.hibernate.event.spi.PostInsertEvent;
import org.hibernate.event.spi.PostUpdateEvent;
import org.hibernate.persister.entity.EntityPersister;
import org.springframework.stereotype.Component;

import net.jlibrary.common.core.annotation.CodeGenComment;
import net.jlibrary.common.core.annotation.OperationType;
import net.jlibrary.common.core.annotation.RecordOperation;
import net.jlibrary.common.core.conversion.JsonUtil;
import net.jlibrary.common.core.entity.BaseObject;
import net.jlibrary.common.core.service.ISessionUser;
import net.jlibrary.common.exception.AuditLogException;
import net.jlibrary.common.exception.ExceptionUtil;
import net.jlibrary.common.project.domain.Account;
import net.jlibrary.common.project.domain.Dictionary;
import net.jlibrary.common.project.domain.OperationField;
import net.jlibrary.common.project.domain.OperationLog;
import net.jlibrary.common.tools.DateUtil;
import net.jlibrary.common.tools.ObjectUtil;
import net.jlibrary.common.tools.RedisUtil;
import redis.clients.jedis.Jedis;

@Component
public class OperationLogListener implements PostCommitInsertEventListener, PostCommitUpdateEventListener {

	private static final long serialVersionUID = 385682654449215032L;

	private final Log log = LogFactory.getLog(getClass());  

	@Resource
	private ISessionUser sessionUser;

	@Override
	public void onPostInsert(PostInsertEvent event) {
		Object entity = event.getEntity();
		
		RecordOperation ro = entity.getClass().getAnnotation(RecordOperation.class);
		if (ro != null) {
			OperationType[] ot = ro.type();
			boolean isRecord = false;
			for (int i = 0; i < ot.length; i++) {
				if (ot[i] == OperationType.SAVE) {
					isRecord = true;
					break;
				}
			}
			if(sessionUser.getSessionAccount() ==null){
				isRecord = false;
			}
			if (isRecord) {
				log.info("实体 " + entity.getClass().getName() + " 需要记录增加日志");
				OperationLog operationLog = new OperationLog();
				operationLog.setAccount((Account) sessionUser.getSessionAccount());
				
				CodeGenComment ec = entity.getClass().getAnnotation(CodeGenComment.class);
				if (ec != null)
					operationLog.setComments(ec.value());
				operationLog.setOperation(OperationLog.OPERATION_SAVE);
				operationLog.setOperationClass(entity.getClass().getName());
				operationLog.setOperationPk(Long.parseLong(((BaseObject) entity).getPrimaryKey() + ""));
				saveOperationLog(event.getSession(), operationLog);

				Field[] fields = ObjectUtil.getClassAndSuperclassFields(entity.getClass());
//				log.info("一共有 " + fields.length + " 列.");
				for (int i = 0; i < fields.length; i++) {
					RecordOperation fieldRecord = fields[i].getAnnotation(RecordOperation.class);
					CodeGenComment fec = fields[i].getAnnotation(CodeGenComment.class);
					if (fieldRecord != null) {// 该列需要记录到日志
//						log.info("需要记录日志的列名是 " + fields[i].getName());
						OperationField of = new OperationField();
						of.setOperationLog(operationLog);
						of.setFieldName(fields[i].getName());
						if (fec != null)
							of.setFieldDesc(fec.value());
						fields[i].setAccessible(true);
						try {
							Object value = fields[i].get(entity);
							if (value instanceof String) {
								String str = (String) value;
								if (str.length() > 500) {
									value = str.substring(0, 500);
								}
							} else if (value instanceof BaseObject) {
								Field[] vfs = ObjectUtil.getClassAndSuperclassFields(value.getClass());
								for (int j = 0; j < vfs.length; j++) {
									vfs[j].setAccessible(true);
									Object cuf = vfs[j].get(value);
									CodeGenComment vf = vfs[j].getAnnotation(CodeGenComment.class);

									if (vf != null && vf.viewText()) {
										if (cuf instanceof String) {
											String str = (String) cuf;
											if (str.length() > 500) {
												value = str.substring(0, 500);
											} else {
												value = str;
											}
										}
										break;
									}
								}
							}
							if (value != null)
								of.setNewValue(value.toString());
						} catch (Exception e) {
							log.error("记录实体 " + entity.getClass().getName() + " 的列 "
									+ fields[i].getName() + " 出现异常", e);
							throw new AuditLogException("记录实体 " + entity.getClass().getName() + " 的列 "
									+ fields[i].getName() + " 出现异常", e);
						}
//						log.info("保存列。。。。。。。。。。。。。。。。。");
						saveOperationLog(event.getSession(), of);
					}
				}
			}
		}
	}

	@Override
	public void onPostUpdate(PostUpdateEvent event) {
		Object entity = event.getEntity();
		if (entity instanceof Dictionary) {
			// 如果是数据字典，则修改到缓存里
			Jedis jedis = RedisUtil.getJedisLocalResource();
			Dictionary d = (Dictionary) entity;
			String key = RedisUtil.getJedisKey(RedisUtil.JEDIS_TYPE_OBJECT, event.getPersister().getEntityName(),
					d.getCode());
			log.debug("字典修改到缓存，KEY是" + key);
			jedis.set(key, JsonUtil.toJson(d));
			RedisUtil.returnResource(jedis);
		}
		RecordOperation ro = entity.getClass().getAnnotation(RecordOperation.class);
		if (ro != null) {
			OperationType[] ot = ro.type();
			boolean isRecord = false;
			for (int i = 0; i < ot.length; i++) {
				if (ot[i] == OperationType.UPDATE) {
					isRecord = true;
					break;
				}
			}
			if(sessionUser.getSessionAccount() ==null){
				isRecord = false;
			}
			isRecord = false;
			if (isRecord) {
				log.info("实体 " + entity.getClass().getName() + " 需要记录修改日志");

				OperationLog operationLog = new OperationLog();
				operationLog.setAccount((Account) sessionUser.getSessionAccount());
				
				CodeGenComment ec = entity.getClass().getAnnotation(CodeGenComment.class);
				if (ec != null)
					operationLog.setComments(ec.value());
				operationLog.setOperation(OperationLog.OPERATION_UPDATE);
				operationLog.setOperationClass(entity.getClass().getName());
				operationLog.setOperationPk(Long.parseLong(((BaseObject) entity).getPrimaryKey() + ""));
				// saveOperationLog(event.getSession(),operationLog);

				Field[] fields = ObjectUtil.getClassAndSuperclassFields(entity.getClass());
				Object[] currentValues = event.getState();// 获取修改后的值
				Object[] oldValues = event.getOldState();// 获取修改前的值
				String[] propertyNames = event.getPersister().getPropertyNames();// 获得属性名称
				List<OperationField> ofs = new ArrayList<OperationField>();// 保存需要记录的字段属性
				for (int i = 0; i < fields.length; i++) {
					try {
						RecordOperation fieldRecord = fields[i].getAnnotation(RecordOperation.class);
						CodeGenComment fec = fields[i].getAnnotation(CodeGenComment.class);
						if (fieldRecord != null) {// 该列需要记录到日志
							for (int j = 0; j < propertyNames.length; j++) {
								// 找到标记了需要记录日志的属性，跟hibernate中的属性做比较，找到同一个
								if (fields[i].getName().equals(propertyNames[j])) {
									// 有修改的才做记录，如果是实体对象，需要比较ID有没有修改
									boolean isFieldRecord = false;
									if (currentValues[j] != null && !currentValues[j].equals(oldValues[j])) {
										isFieldRecord = true;
									}
									if (oldValues[j] != null && !oldValues[j].equals(currentValues[j])) {
										isFieldRecord = true;
									}
									if (isFieldRecord) {
										if (oldValues[j] instanceof BaseObject) {
											BaseObject old = (BaseObject) oldValues[j];
											BaseObject current = (BaseObject) currentValues[j];
											if (old!=null && current!=null && old.getPrimaryKey()!=null && current.getPrimaryKey()!=null &&old.getPrimaryKey().equals(current.getPrimaryKey())) {
												isFieldRecord = false;
											}
										}
									}
									if (isFieldRecord) {
										OperationField of = new OperationField();
										of.setOperationLog(operationLog);
										of.setFieldName(fields[i].getName());
										if (fec != null)
											of.setFieldDesc(fec.value());

										Object currentValue = currentValues[j];
										Object oldValue = oldValues[j];
										if (currentValue instanceof String) {
											String str = (String) currentValue;
											if (str.length() > 500) {
												currentValue = str.subSequence(0, 500);
											}
										} else if (currentValue instanceof BaseObject) {
											BaseObject baseObject = (BaseObject) ObjectUtil
													.getRealClassName(currentValue);
											log.debug("保存日志 currentValue 是实体对象--->" + baseObject.getClass().getName());

											Field[] vfs = ObjectUtil.getClassAndSuperclassFields(baseObject.getClass());
											for (int m = 0; m < vfs.length; m++) {
												vfs[m].setAccessible(true);
												Object cuf = vfs[m].get(baseObject);
												CodeGenComment vf = vfs[m].getAnnotation(CodeGenComment.class);
												if (vf != null && vf.viewText()) {
													if (cuf instanceof String) {
														String str = (String) cuf;
														if (str.length() > 500) {
															currentValue = str.substring(0, 500);
														} else {
															currentValue = str;
														}
													}
													break;
												}
											}
										}

										if (oldValue instanceof String) {
											String str = (String) oldValue;
											if (str != null && str.length() > 500) {
												oldValue = str.subSequence(0, 500);
											}
										} else if (oldValue instanceof BaseObject) {
											BaseObject baseObject = (BaseObject) ObjectUtil.getRealClassName(oldValue);
											log.debug("保存日志 oldValue 是实体对象--->" + baseObject.getClass().getName());

											Field[] vfs = ObjectUtil.getClassAndSuperclassFields(baseObject.getClass());
											for (int m = 0; m < vfs.length; m++) {
												vfs[m].setAccessible(true);
												Object cuf = vfs[m].get(baseObject);
												CodeGenComment vf = vfs[m].getAnnotation(CodeGenComment.class);
												if (vf != null && vf.viewText()) {
													if (cuf instanceof String) {
														String str = (String) cuf;
														if (str!= null && str.length() > 500) {
															oldValue = str.substring(0, 500);
														} else {
															oldValue = str;
														}
													}
													break;
												}
											}
										}
										if (currentValue != null)
											of.setNewValue(currentValue.toString());
										if (oldValue != null)
											of.setOldValue(oldValue.toString());
										ofs.add(of);
									}
									break;
								}
							}
						}
					} catch (Exception e) {
						log.error("记录实体 " + entity.getClass().getName() + " 的列 "
								+ fields[i].getName() + " 出现异常 -> " + ExceptionUtil.stackTraceToString(e, null));
						throw new AuditLogException("记录实体 " + entity.getClass().getName() + " 的列 "
								+ fields[i].getName() + " 出现异常", e);
					}
				}

				if (ofs.size() > 0) {
					// 如果有超过一个及以上的字段发生改变，则记录日志
					saveOperationLog(event.getSession(), operationLog);
					for (OperationField operationField : ofs) {
						saveOperationLog(event.getSession(), operationField);
					}
				}
			}
		}
	}

	

	@Override
	public boolean requiresPostCommitHanding(EntityPersister persister) {
		log.info("requiresPostCommitHanding");
		return true;
	}

	private void saveOperationLog(Session session, BaseObject bo) {
		Session se = session.getSessionFactory().openSession();
		Transaction tx = null;
		try {
			tx = se.beginTransaction();
			bo.setCreatedate(DateUtil.getCurrentTime());
			bo.setLastdate(DateUtil.getCurrentTime());
			se.save(bo);
			tx.commit();
		} catch (Exception e) {
			log.error("保存日志出现异常 -> "  + ExceptionUtil.stackTraceToString(e, null));
			tx.rollback();
		} finally {
			se.close();
		}
	}
	
	@Override
	public void onPostInsertCommitFailed(PostInsertEvent event) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void onPostUpdateCommitFailed(PostUpdateEvent event) {
		// TODO Auto-generated method stub
		
	}
}
